#ifndef FRUSTUM_H
#define FRUSTUM_H

//#include "TwTypeHead.h"
//#include "twplane3d.h"
//#include "twrect3d.h"
//#include "twsphere.h"
#include "twintersection_tests.h"


class TwFrustum
{
public:

	//--------------------------------------------------
	// Public Data...
	//--------------------------------------------------
	TwPlane3d leftPlane;
	TwPlane3d rightPlane;
	TwPlane3d topPlane;
	TwPlane3d bottomPlane;
	TwPlane3d nearPlane;
	TwPlane3d farPlane;

	//--------------------------------------------------
	// Creators...
	//--------------------------------------------------
    TwFrustum();
    TwFrustum( const TwFrustum& Src );
    ~TwFrustum();

	//--------------------------------------------------
	// Operators...
	//--------------------------------------------------
	TwFrustum& operator=( const TwFrustum& Src );
	bool operator==( const TwFrustum& Src);

	//--------------------------------------------------
	// Mutators...
	//--------------------------------------------------
	void extractFromMatrix(
		const TwMatrix4x4& matrix,
		bool normalizePlanes = false);

 	//--------------------------------------------------
	// Test Operations...
	//--------------------------------------------------
	bool testPoint(const TwVector3d& point) const;
	bool testRect(const TwRect3d& rect) const;
	bool testSphere(const TwSphere& sphere) const;
};

//- Inline Functions ---------------------------------------------------------------------

//= CREATORS =============================================================================

/*	TwFrustum
------------------------------------------------------------------------------------------
	
	Default Constructor
	
------------------------------------------------------------------------------------------
*/
inline TwFrustum::TwFrustum()
{
}

/*	TwFrustum
------------------------------------------------------------------------------------------
	
	Default Copy Constructor
	
------------------------------------------------------------------------------------------
*/
inline TwFrustum::TwFrustum(const TwFrustum& src)
:leftPlane(src.leftPlane)
,rightPlane(src.rightPlane)
,topPlane(src.topPlane)
,bottomPlane(src.bottomPlane)
,nearPlane(src.nearPlane)
,farPlane(src.farPlane)
{
}


inline TwFrustum::~TwFrustum()
{
}

//= OPERATORS ============================================================================


inline TwFrustum& TwFrustum::operator=( const TwFrustum& src )
{
	leftPlane = src.leftPlane;
	rightPlane = src.rightPlane;
	topPlane = src.topPlane;
	bottomPlane = src.bottomPlane;
	nearPlane = src.nearPlane;
	farPlane = src.farPlane;

	return (*this);
}

inline bool TwFrustum::operator==(const TwFrustum& src )
{
	return (leftPlane == src.leftPlane)
		&&(rightPlane == src.rightPlane)
		&&(topPlane == src.topPlane)
		&&(bottomPlane == src.bottomPlane)
		&&(nearPlane == src.nearPlane)
		&&(farPlane == src.farPlane);
}

//= MUTATORS =============================================================================

inline void TwFrustum::extractFromMatrix(
	const TwMatrix4x4& matrix,
	bool normalizePlanes)
{
	// Left clipping plane 
	leftPlane.normal.x = matrix._14 + matrix._11; 
	leftPlane.normal.y = matrix._24 + matrix._21; 
	leftPlane.normal.z = matrix._34 + matrix._31; 
	leftPlane.distance = matrix._44 + matrix._41;

	// Right clipping plane 
	rightPlane.normal.x = matrix._14 - matrix._11; 
	rightPlane.normal.y = matrix._24 - matrix._21; 
	rightPlane.normal.z = matrix._34 - matrix._31; 
	rightPlane.distance = matrix._44 - matrix._41;

	// Top clipping plane 
	topPlane.normal.x = matrix._14 - matrix._12; 
	topPlane.normal.y = matrix._24 - matrix._22; 
	topPlane.normal.z = matrix._34 - matrix._32; 
	topPlane.distance = matrix._44 - matrix._42;

	// Bottom clipping plane 
	bottomPlane.normal.x = matrix._14 + matrix._12; 
	bottomPlane.normal.y = matrix._24 + matrix._22; 
	bottomPlane.normal.z = matrix._34 + matrix._32; 
	bottomPlane.distance = matrix._44 + matrix._42;

	// Near clipping plane 
	nearPlane.normal.x = matrix._13; 
	nearPlane.normal.y = matrix._23; 
	nearPlane.normal.z = matrix._33; 
	nearPlane.distance = matrix._43;

	// Far clipping plane 
	farPlane.normal.x = matrix._14 - matrix._13; 
	farPlane.normal.y = matrix._24 - matrix._23; 
	farPlane.normal.z = matrix._34 - matrix._33; 
	farPlane.distance = matrix._44 - matrix._43; 

	// it is not always nessesary to normalize
	// the planes of the frustum. Non-normalized
	// planes can still be used for basic
	// intersection tests.
	if (normalizePlanes)
	{
		leftPlane.normalize();
		rightPlane.normalize();
		topPlane.normalize();
		bottomPlane.normalize();
		nearPlane.normalize();
		farPlane.normalize();
	}
}

//= TESTS =============================================================================
inline bool TwFrustum::testPoint(const TwVector3d& point) const
{
	if ((leftPlane.signedDistance(point)		< 0.0f)
		|| (rightPlane.signedDistance(point)	< 0.0f)
		|| (topPlane.signedDistance(point)		< 0.0f)
		|| (bottomPlane.signedDistance(point)	< 0.0f)
		|| (nearPlane.signedDistance(point)		< 0.0f)
		|| (farPlane.signedDistance(point)		< 0.0f))
	{
		return false;
	}

	return true;
}

inline bool TwFrustum::testRect(
	const TwRect3d& rect) const
{

	if ((planeClassify(rect, leftPlane)		
			== k_plane_back)
		|| (planeClassify(rect, rightPlane) 
			== k_plane_back)
		|| (planeClassify(rect, topPlane)	
			== k_plane_back)
		|| (planeClassify(rect, bottomPlane)
			== k_plane_back)
		|| (planeClassify(rect, nearPlane)	
			== k_plane_back)
		|| (planeClassify(rect, farPlane)	
			== k_plane_back))
	{
		return false;
	}
	
	return true;
}

inline bool TwFrustum::testSphere(const TwSphere& sphere) const
{

	if ((planeClassify(sphere, leftPlane)		== k_plane_back)
		|| (planeClassify(sphere, rightPlane)	== k_plane_back)
		|| (planeClassify(sphere, topPlane)		== k_plane_back)
		|| (planeClassify(sphere, bottomPlane)	== k_plane_back)
		|| (planeClassify(sphere, nearPlane)	== k_plane_back)
		|| (planeClassify(sphere, farPlane)		== k_plane_back))
	{
		return false;
	}
	
	return true;
}

#endif  

